import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ChangeDetectorRef,
  OnDestroy,
  AfterViewInit,
  OnChanges,
  SimpleChanges,
  Renderer2,
  ElementRef,
} from '@angular/core';
import { Subject, of, merge, Observable } from 'rxjs';
import { filter, takeUntil } from 'rxjs/operators';
import {
  isObject as _isObject,
  assign as _assign,
  isNil as _isNil,
  filter as _filter,
  cloneDeep as _cloneDeep,
} from 'lodash';
import { InputBoolean } from '../core/api';
import { ContextService } from '../core/services/context.service';
import { DatatableService } from './datatable.service';
import { initCheckboxError, initCheckboxRowDataError } from './datatable-errors';

@Component({
  selector: 'td[lv-td], td[lvTd], td',
  exportAs: 'lvTd',
  templateUrl: './td.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [ContextService],
  host: {
    '[class.lv-table-td]': '_parentComponent',
    '[class.lv-table-td-inner]': 'lvInnerUnfold',
    '[style.textAlign]': 'lvAlign',
    '[class.lv-table-sticky-left-cell]': 'lvLeft',
    '[class.lv-table-sticky-right-cell]': 'lvRight',
    '[style.left]': 'lvLeft',
    '[style.right]': 'lvRight',
  },
})
export class TdComponent implements OnInit, OnDestroy, AfterViewInit, OnChanges {
  /**
   * expanded unfold 区分
   * unfold：指表格展开详情
   * expanded：指树表展开子项
   */
  @Input() lvCheckboxTip = ''; // 复选框提示信息
  // 输入输出属性
  @Input() lvLeft: string | boolean; // 固定列相对于左对齐 NEED_DO :后续支持自动计算
  @Input() lvRight: string | boolean; // 固定列相对于右对齐 NEED_DO :后续支持自动计算
  @Input() @InputBoolean() lvUnfold = false; // 展开子项状态
  @Input() lvRowData: { [key: string]: any }; // 行数据
  @Input() @InputBoolean() lvExpanded = false; // 展开子项状态
  @Input() @InputBoolean() lvDisabled = false; // 复选框是否可点
  @Input() @InputBoolean() lvShowUnfold = false; // 是否显示展开详情icon

  @Input() @InputBoolean() lvUnfoldEdit = false; // 是否为复杂编辑状态
  @Input() @InputBoolean() lvInnerUnfold = false; // 是否为内嵌的内容
  @Input() @InputBoolean() lvShowExpanded = false; // 是否显示展开子项icon

  @Input() @InputBoolean() lvShowCheckbox = false; // 是否展示复选选框
  // @Input() lvRowsData: { [key: string]: any }[]; // 多行子数据
  @Input() lvPaddingLeft: number | string = '0px'; // 自定义左边距，用于树表场景
  @Input() @InputBoolean() lvKeepWhiteSpace: boolean; // 是否保留字符的所有空格
  @Input() lvAlign: 'left' | 'right' | 'center' = 'left'; // 标题对齐方式
  @Output() lvUnfoldChange = new EventEmitter<boolean>(); // 展开详情回调事件
  @Output() lvExpandedChange = new EventEmitter<boolean>(); // 展开子项回调事件

  @Output() lvCheckChange = new EventEmitter<{ [key: string]: any }>(); // 返回选中项
  @Input() lvUnfoldToggleIcon: { true?: string; false?: string } | string; // 展开详情切换icon
  @Input() lvExpandedToggleIcon: { true?: string; false?: string } | string; // 展开子项切换icon

  _parentComponent;

  // 外部可访问变量
  innerPaddingLeft: string = '0';

  unfoldChange$ = new Subject<boolean>();

  // 内部变量
  private _destroy$ = new Subject<void>();

  private _rowsDataChanged: boolean = false; // 判断是否lvRowsData设置的标记
  private _rowsData: Array<any>; // NEED_DO: 未用到

  private _defaultUnfoldIcon = { true: 'lv-icon-arrow-next', false: 'lv-icon-arrow-next' };

  private _defaultExpandedIcon = { true: 'lv-icon-tree-unfold', false: 'lv-icon-tree-fold' };

  constructor(
    private _cdr: ChangeDetectorRef,
    private service: DatatableService,
    private render: Renderer2,
    private elementRef: ElementRef,
    public contextService: ContextService,
  ) {
    this._parentComponent = this.service.getParentComponent();
    this._subscribeInitError();
  }

  get multiple(): boolean {
    return !!this._parentComponent?.selectionMultiMode;
  }

  get _renderSelection(): any {
    return this._parentComponent?.getRenderSelection();
  }

  get unfoldToggleIcon(): any {
    return this._getToggleIcon(this.lvUnfoldToggleIcon, this._defaultUnfoldIcon);
  }

  get expandedToggleIcon(): any {
    return this._getToggleIcon(this.lvExpandedToggleIcon, this._defaultExpandedIcon);
  }

  get isHalfCheck(): boolean {
    if (!this._parentComponent || !this._parentComponent.lvSelectionAssociate) {
      return false;
    }
    const selectedChild = !!this._selectedChildren(true).length;
    return !this.isAllCheck && selectedChild;
  }

  /**
   * 场景：行选中有关联时
   * 全选：
   * 1. 自己被选中
   * 2. 子级全部被选中
   * 半选：
   * 1. 不是全选并有子被选中
   */
  get isAllCheck(): any {
    if (!this._parentComponent) {
      return false;
    }
    const selected = this._isSelected(this.lvRowData);
    const rows = this._rowsData || [];
    const selectedChildren = !!rows.length && this._selectedChildren().length === rows.length;
    return selected || selectedChildren;
  }

  ngAfterViewInit(): void {
    this._renderView();
  }

  getUnfoldFlow(): Observable<any> {
    return this.unfoldChange$.asObservable();
  }

  _isSelected(source: any): any {
    return this._parentComponent.isSelected(source);
  }

  /**
   * 监听父组件的renderdata变换（多选情况下在没设置lvRowsData前提下
   * renderdata --update--> rowsdata 为了更新全选比较源数据
   */
  _subscribeRenderData(): void {
    this._rowsData = this.lvRowData?.children;
  }

  setWidth(width: number): void {
    this.render.setStyle(this.elementRef.nativeElement, 'width', `${width}px`);
  }

  ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
    this.unfoldChange$.complete();
  }

  unfold(e: MouseEvent): void {
    e.stopPropagation();
    this.lvUnfold = !this.lvUnfold;
    this.unfoldChange$.next(true);
    this.lvUnfoldChange.emit(this.lvUnfold);
  }

  expanded(e: MouseEvent): void {
    e.stopPropagation();
    this.lvExpanded = !this.lvExpanded;
    this.unfoldChange$.next(true);
    this.lvExpandedChange.emit(this.lvExpanded);
  }

  /**
   * 获取toggleIcon
   * source === undefined | null 默认原始icon
   * source === string 默认toggle为同一icon
   * source === obj 覆盖原始
   */
  _getToggleIcon(source: any, origin: any): any {
    let output = source;
    if (_isNil(source)) {
      return origin;
    }
    if (typeof source === 'string') {
      output = { true: source, false: source };
    }
    return _assign(origin, output);
  }

  ngOnInit(): void {
    this.lvKeepWhiteSpace =
      this.lvKeepWhiteSpace === undefined ? this._parentComponent?.lvKeepWhiteSpace : this.lvKeepWhiteSpace;
    // 表格td局部配置 > 表格datatable局部配置 > 表格全局配置
    this.contextService.keepWhiteSpace = this.lvKeepWhiteSpace;
    this._subscribeRenderData();
  }

  /**
   * 切换当前行选中状态NEED_DO: 优化逻辑
   * @param source
   * 特殊场景：行选中有联动关系时（树表），行选中影响父子行
   * 1. 触发父及祖先选中
   * 2. 触发子及后代选中
   */
  _selectionRow(e: MouseEvent, source: any): void {
    e.stopPropagation();
    if (this.lvDisabled) {
      return;
    }

    // 切换选中项并触发change
    this._parentComponent.toggleSelection(source);
    this.lvCheckChange.emit(this._isSelected(source) ? source : null);

    this.service.setCheckboxStream({
      key: 'td',
      check: this.isAllCheck,
      halfCheck: this.isHalfCheck,
    });
  }

  _subscribeInitError(): void {
    if (!this._parentComponent) {
      return;
    }

    if (this.lvShowCheckbox) {
      // 单选模式不能设置checkbox
      if (!this.multiple) {
        throw initCheckboxError('td', 'lvShowCheckbox');
      }

      // 多选模式必须设置lvRowData
      if (!_isObject(this.lvRowData)) {
        throw initCheckboxRowDataError('td', 'lvRowData');
      }

      // 数据类型不对lvRowsData
    }
  }

  // 全选时traverse为false，只遍历子集不遍历后代，半选时traverse为true，需要遍历所有后代
  _selectedChildren(traverse: boolean = false): any {
    // selection的地址发生变更，使用compareWith值比较
    // 不直接使用_rowsData去获取dataMap，再次获取selectedChildren时，resultToDataMap方法会造成_rowsData中parent的获取异常
    const data = _cloneDeep(this._rowsData) || [];
    const dataMap = this._parentComponent.resultToDataMap(data, traverse);
    return _filter(this._renderSelection, (item): any => dataMap.has(this._parentComponent.result(item)));
  }

  ngOnChanges(changes: SimpleChanges): void {
    const rowsDataChange = changes.lvRowsData;
    if (rowsDataChange) {
      this._rowsDataChanged = true;
      this._rowsData = rowsDataChange.currentValue || [];
    }

    const paddingLeftChange = changes.lvPaddingLeft;
    if (paddingLeftChange) {
      let left = paddingLeftChange.currentValue;
      if (typeof left === 'number') {
        left = `${left}px`;
      }
      this.innerPaddingLeft = left;
    }

    const unfoldChange = changes.lvUnfold;
    if (unfoldChange?.previousValue !== undefined) {
      this.unfoldChange$.next(true);
    }
  }

  /**
   * 1. 更新选中项
   * th checkbox流变更
   * 2. 更新视图
   * 触发td checkbox状态：
   * th checkbox流变更
   * table selection流变更
   */
  _renderView(): void {
    // 只有checkbox的th才监听
    if (!(this.lvShowCheckbox && this.multiple)) {
      return;
    }
    const selectionStream = this._parentComponent
      ? // tslint:disable-next-line
        merge(this._parentComponent.selectionChange$, this._parentComponent.lvSelectionChange)
      : of(null);
    const toggleCheckThStream = this.service
      .getCheckboxStream()
      .pipe(filter((res): boolean => (res as any).key === 'th'));
    // 1 待实现：toggleCheckThStream

    // 2
    merge(toggleCheckThStream, selectionStream)
      .pipe(takeUntil(this._destroy$))
      .subscribe((): void => {
        this._cdr.markForCheck();
      });
  }
}
